/*

目标：  解析设备树描述中断节点，获取中断号

测试电源按键： 
如何在驱动中解析：
interrupt-parent = <&gpio0>;//对应的上级中断控制器是 gpio0
		interrupts = <5 IRQ_TYPE_EDGE_BOTH>; //GPIO引脚号 触发方式 GPIO0 A5

		获取中断号，解析上面以后，函数返回值就是中断号

	
/ {
	...
	
	//add pwr
	pwr_button {
		compatible = "rk3399,power-button";
		gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
		interrupt-parent = <&gpio0>;//对应的上级中断控制器是 gpio0
		interrupts = <5 IRQ_TYPE_EDGE_BOTH>; //GPIO引脚号 触发方式 GPIO0 A5
	};
	...
};

大板: rk3399-nanopi4-rev07.dts

*/


#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of.h> // of				
#include <linux/of_gpio.h> //gpio	
#include <linux/gpio.h>	
#include <dt-bindings/gpio/gpio.h>
#include <linux/interrupt.h>

static int gpio_button;
static int irqno;

//定义中断处理函数
static irqreturn_t button_handler(int irqno, void *dev)
{
	
	printk("<kernel>call %s()\n", __func__); 
	return IRQ_HANDLED;
}


//当平台驱动和平台设备匹配时调用probe函数
/**
 * 通过pdev 获取设备树节点属性
 */
static int button_probe(struct platform_device *pdev)
{

	/*
	[  239.986825] gpio_button=5
[  239.987072] irqno=75
	*/

	int ret;
	
	//获得匹配的device_node对象, 然后拿设备节点下硬件数据
	struct device_node *button_ctrl_node = pdev->dev.of_node;
	gpio_button = of_get_named_gpio(button_ctrl_node, "gpios", 0);
	if (gpio_is_valid(gpio_button)) {
		printk("gpio_button=%d\n", gpio_button);
	} else {
		printk("of_get_named_gpio_flags fail\n");
		return -EINVAL;
	}
	/**
	 * 中断处理方式1:首先解析gpios节点获取GPIO编号, 然后通过gpio_to_irq()函数获取中断号
	 */

	irqno = gpio_to_irq(gpio_button);
	printk("irqno=%d\n", irqno);

		/**
	 * 中断处理方式2:直接解析interrupts节点获取中断号
	 *  这些api是学习plat_devices 的时候用到的, 为了兼容老的platform_device而设计的
	 */
#if 0
	struct resource *irq_res;

	//通过中断资源来拿中断号,通过platform_get_resource 或者 platform_get_irq()函数获取中断号	
	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	irqno = irq_res->start;
	printk("irqno=%d\n", irqno);

	printk("irqno=%d\n", platform_get_irq(pdev, 0));
#endif 

 /**
  *  方式3 解析中断节点获取中断号
  */

#if 0 
 int trigger_type;
 //从设备树解析中断号
	irqno = of_irq_get(pdev->dev.of_node, 0);
	if (irqno <= 0) {
		printk("of_irq_get fail\n");
		return -EINVAL;
	} 
	printk("irqno=%d\n", irqno);

	//获取触发类型
	trigger_type = irq_get_trigger_type(irqno);
	if (trigger_type == IRQ_TYPE_EDGE_BOTH)
		printk("上下双边沿触发方式\n");
#endif 


	ret = request_irq(irqno, button_handler, IRQ_TYPE_EDGE_BOTH, "pwr_key_Int", NULL);
	if (ret) {
		printk("request_irq fail\n");
		return -EFAULT;
	}



	printk("<kernel> call %s()\n", __func__);  
	return 0;
}

//当平台驱动和平台设备分离时调用remove函数
static int button_remove(struct platform_device *pdev)
{
	free_irq(irqno, NULL);
	printk("<kernel> call %s()\n", __func__);  
	return 0;

}

static const struct platform_device_id button_id_table[] = {
	{
		.name = "rk3399_button",
		.driver_data = 0x1,
	}, {
		.name = "rk3288_button",
		.driver_data = 0x2,
	}, {
		.name = "rk3568_button",
		.driver_data = 0x3,
	}, {
		/* id table */
	}
};

static const struct of_device_id button_of_match[] = {
	{ .compatible = "rk3399,power-button", },  //必须和设备树节点的compatible属性匹配
	{ /*of match table*/ }
};


//定义平台驱动对象并初始化
static struct platform_driver rk_button_driver = {
	.probe		= button_probe,
	.remove		= button_remove,
	.driver		= {
		.name	= "rk_button", //平台驱动的字符串名称
		.of_match_table = button_of_match, //指向匹配设备树节点的数组
	},
	.id_table = button_id_table, //指向匹配平台设备的数组
};



static int __init driver_mod_init(void)
{
	printk("<kernel> call %s()\n", __func__);  
	return platform_driver_register(&rk_button_driver);
}


static void __exit driver_mod_exit(void)
{
	printk("<kernel> call %s() @ %d\n", __FUNCTION__, __LINE__);  
	platform_driver_unregister(&rk_button_driver);
}


module_init(driver_mod_init); 
module_exit(driver_mod_exit); 
MODULE_LICENSE("GPL"); 



